#pragma once

#include <string>
#include <jsoncpp/json/json.h>
#include <mysql/mysql.h>
#include <sstream>
#include <fstream>
#include <vector>
#include "log.hpp"



class json_util// 反序列化和序列化工具类
{
public:
    // 序列化
    static bool serialize(Json::Value root,std::string &out)// Value结构序列化成string类
    {
        Json::StreamWriterBuilder swb;// 创建一个工厂类
        Json::StreamWriter *sw = swb.newStreamWriter();// 生产一个文件流对象

        // 序列化
        std::stringstream ss;// 字符串流
        int n = sw->write(root,&ss); // 直接把Json::Value对象写入stringstream对象当中，从而不需要在内存中保存
        if(n != 0)
        {
            ERROR_LOG("序列化失败,n=%d",n);
            return false;
        }
        out = ss.str();// 从流当中拿出Json格式的序列化字符串
        return true;
    }
    
    // 反序列化
    static bool deserialize(const std::string &in,Json::Value &out)// 将string对象序列化成Json::Value对象
    {
        Json::CharReaderBuilder crb;// 工厂类
        Json::CharReader *cr = crb.newCharReader();// 生产一个CharReader对象

        // 反序列化
        std::string err;
        bool ret = cr->parse(in.c_str(),in.c_str()+in.size(),&out,&err);// 解析字符串，将反序列化的结果写到Json::Value对象当中
        if(ret == false)
        {
            ERROR_LOG("序列化失败,err:%s",err.c_str());
            return false;
        }
        return true;
    }
};


class file_util// 文件工具类
{
public:
    static bool read_file(const std::string &path,std::string &out)// 读文件
    {
        //1. 以二进制的方式打开文件
        std::ifstream in(path,std::ios::binary);
        if(!in.is_open())
        {
            ERROR_LOG("文件打开失败！文件路径:%s",path.c_str());
            return false;
        }
        //2. 得到该文件的大小
        in.seekg(0,std::ios::end);
        size_t file_size = in.tellg();
        in.seekg(0,std::ios::beg);
        out.resize(file_size);

        //3. 将文件内容读出
        in.read(&out[0],file_size);
        //4. 关闭打开的文件
        in.close();
        return true;
    }
};

#define SERVER_IP "127.0.0.1"
#define USER_NAME "ly"
#define USER_PASSWORD "20020214"
#define DATABASE_NAME "gobang_cpp"
#define PORT 3306
class mysql_util// mysql工具类
{
public:
    //1. 初始化
    static MYSQL *init()
    {
        
        MYSQL *mysql = mysql_init(nullptr);// 初始化句柄
        if(mysql == nullptr)
        {
            ERROR_LOG("初始化MYSQL句柄失败!");
            return mysql;
        }

        mysql = mysql_real_connect(mysql,SERVER_IP,USER_NAME,USER_PASSWORD,DATABASE_NAME,PORT,nullptr,0);// 连接服务器
        if(mysql == nullptr)
        {
            ERROR_LOG("连接服务器失败!");
            return mysql;
        }
        
        int n = mysql_set_character_set(mysql,"utf8");// 设置字符集，如果不设置中文就会乱码
        if(n != 0)
        {
            ERROR_LOG("设置字符集失败!");
            return mysql;
        }
        return mysql;// 返回句柄
    } 
    
    static bool query(MYSQL *mysql,const std::string &sql)// 执行sql语句
    {
        int n = mysql_query(mysql,sql.c_str());// 什么句柄执行什么语句
        if(n != 0)
        {
            ERROR_LOG("sql语句执行失败!");
            return false;
        }
        return true;
    }
    
    static bool destroy(MYSQL *mysql)// 销毁
    {
        if(mysql != nullptr)// 不为空才销毁
        {
            mysql_close(mysql);
            return true;
        }
        ERROR_LOG("MYSQL句柄销毁失败!MYSQL句柄为空!");
        return false;
    }
};



class string_util// 字符串解析工具类
{
public:
    static bool split(const std::string &in,const std::string &sep,std::vector<std::string> &out)// 字符串分割
    {
        if(in.empty())// 为空不解析
        {
            ERROR_LOG("字符串解析失败：无法解析空串!");
            return false;
        }
        // if(in.find(sep,0) == std::string::npos)
        // {
        //     ERROR_LOG("字符串解析失败,无法找到指定的%s",sep.c_str());
        //     return false;
        // }
        
        size_t begin = 0,end = 0;
        while(begin < in.size())
        {
            end = in.find(sep,begin);
            if(end == std::string::npos)
            {
                out.push_back(in.substr(begin));
                break;// 从begin位置开始往后没有sep，查找结束
            }
            if(end == begin)// 说明begin位置就是sep，跳过
            {
                begin += sep.size();
                end += sep.size();
                continue;
            }
            out.push_back(in.substr(begin,end - begin));// 找到了就放进结果集当中
            end += sep.size();
            begin = end;
        }
        return true;
    }
};